﻿// GB28181Dialog.cpp: 实现文件
//

#include "pch.h"
#include "MFCFFMPEG.h"
#include "GB28181Dialog.h"
#include "afxdialogex.h"
#include <iostream>
#include <list>
#include <string>
#include <sstream>

#include <winsock2.h>
#include <ws2tcpip.h>
#include <Iphlpapi.h>
//#pragma comment (lib, "Ws2_32.lib")
//#pragma comment(lib,"Iphlpapi.lib")
#pragma comment(lib, "libmpeg.lib")

using namespace std;

extern BOOL GetLocalAdaptersInfo(list<string>& ips);

// GB28181Dialog 对话框

IMPLEMENT_DYNAMIC(GB28181Dialog, CDialogEx)

GB28181Dialog::GB28181Dialog(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG_GB28181, pParent)
	, m_port(0)
	, m_RTPPSPT(0)
	, m_RTP_PT_H264(0)
	, m_RTP_PT_H265(0)
	, m_pthread(NULL)
{

}

GB28181Dialog::~GB28181Dialog()
{
}

void GB28181Dialog::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_COMBO_MYIPS, m_myips);
	DDX_Text(pDX, IDC_EDIT_MYPORT, m_port);
	DDV_MinMaxInt(pDX, m_port, 10, 65535);
	DDX_Text(pDX, IDC_EDIT_RTP_PT_PS, m_RTPPSPT);
	DDV_MinMaxInt(pDX, m_RTPPSPT, 20, 65535);
	DDX_Text(pDX, IDC_EDIT_RTP_PT_H264, m_RTP_PT_H264);
	DDX_Text(pDX, IDC_EDIT_RTP_PT_H265, m_RTP_PT_H265);
	DDV_MinMaxInt(pDX, m_RTP_PT_H265, 11, 65535);
}


BEGIN_MESSAGE_MAP(GB28181Dialog, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON_START, &GB28181Dialog::OnBnClickedButtonStart)
END_MESSAGE_MAP()


// GB28181Dialog 消息处理程序
BOOL GB28181Dialog::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化
	SetDlgItemText(IDC_EDIT_MYPORT, L"6002");
	SetDlgItemText(IDC_EDIT_RTP_PT_PS, L"96");
	SetDlgItemText(IDC_EDIT_RTP_PT_H264, L"98");
	SetDlgItemText(IDC_EDIT_RTP_PT_H265, L"97");

	//本机IP
	m_myips.ResetContent();
	list<string> ips;
	GetLocalAdaptersInfo(ips);
	list<string>::iterator itr;
	for (itr = ips.begin(); itr != ips.end(); itr++)
	{
		USES_CONVERSION;
		CString ipaddr = A2T((*itr).c_str());
		m_myips.AddString(ipaddr);
	}
	if (ips.size() > 0) {
		m_myips.SetCurSel(0);
	}
	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

class MyInfo {
public:
	char ip[30];
	int port;

	MyInfo(const char* ip, int port)
	{
		strcpy(this->ip, ip);
		this->port = port;
	}
};

int bindSock(int sockFd, const char* ifr_ip, uint16_t port) {
	struct sockaddr_in servaddr;
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	servaddr.sin_addr.s_addr = inet_addr(ifr_ip);
	memset(&(servaddr.sin_zero),0, sizeof servaddr.sin_zero);

	//绑定监听
	if (bind(sockFd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
		std::cout << "绑定套接字失败:" << WSAGetLastError();
		return -1;
	}
	return 0;
}

#define RTP_MAX_SIZE (10 * 1024)

#if defined(_WIN32)
#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
#define BYTE_ORDER LITTLE_ENDIAN
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#endif

#if defined(_WIN32)
#pragma pack(push, 1)
#endif // defined(_WIN32)

class RtpHeader {
public:
#if __BYTE_ORDER == __BIG_ENDIAN
	//版本号，固定为2
	uint32_t version : 2;
	//padding
	uint32_t padding : 1;
	//扩展
	uint32_t ext : 1;
	//csrc
	uint32_t csrc : 4;
	//mark
	uint32_t mark : 1;
	//负载类型
	uint32_t pt : 7;
#else
	//csrc
	uint32_t csrc : 4;
	//扩展
	uint32_t ext : 1;
	//padding
	uint32_t padding : 1;
	//版本号，固定为2
	uint32_t version : 2;
	//负载类型
	uint32_t pt : 7;
	//mark
	uint32_t mark : 1;
#endif
	//序列号
	uint32_t seq : 16;
	//时间戳
	uint32_t stamp;
	//ssrc
	uint32_t ssrc;
	//负载，如果有csrc和ext，前面为 4 * csrc + (4 + 4 * ext_len)
	uint8_t payload;

	enum {
		kRtpVersion = 2,
		kRtpHeaderSize = 12,
		kRtpTcpHeaderSize = 4
	};

public:
	//返回csrc字段字节长度
	size_t getCsrcSize() const;
	//返回csrc字段首地址，不存在时返回nullptr
	uint8_t* getCsrcData();

	//返回ext字段字节长度
	size_t getExtSize() const;
	//返回ext段首地址，不存在时返回nullptr
	uint8_t* getExtData();

	//返回有效负载指针,跳过csrc、ext
	uint8_t* getPayloadData();
	//返回有效负载总长度,不包括csrc、ext、padding
	size_t getPayloadSize(size_t rtp_size) const;
	//打印调试信息
	string dumpString(size_t rtp_size) const;

private:
	//返回有效负载偏移量
	size_t getPayloadOffset() const;
	//返回padding长度
	size_t getPaddingSize(size_t rtp_size) const;
} PACKED;

#if defined(_WIN32)
#pragma pack(pop)
#endif // defined(_WIN32)

#define AV_RB16(x)                           \
    ((((const uint8_t*)(x))[0] << 8) |          \
      ((const uint8_t*)(x))[1])

size_t RtpHeader::getCsrcSize() const {
	//每个csrc占用4字节
	return csrc << 2;
}

uint8_t* RtpHeader::getCsrcData() {
	if (!csrc) {
		return nullptr;
	}
	return &payload;
}

size_t RtpHeader::getExtSize() const {
	//rtp有ext
	if (!ext) {
		return 0;
	}
	auto ext_ptr = &payload + getCsrcSize();
	uint16_t reserved = AV_RB16(ext_ptr);
	//每个ext占用4字节
	return AV_RB16(ext_ptr + 2) << 2;
}

uint8_t* RtpHeader::getExtData() {
	if (!ext) {
		return nullptr;
	}
	auto ext_ptr = &payload + getCsrcSize();
	//多出的4个字节分别为reserved、ext_len
	return ext_ptr + 4 + getExtSize();
}

size_t RtpHeader::getPayloadOffset() const {
	//有ext时，还需要忽略reserved、ext_len 4个字节
	return getCsrcSize() + (ext ? (4 + getExtSize()) : 0);
}

uint8_t* RtpHeader::getPayloadData() {
	return &payload + getPayloadOffset();
}

size_t RtpHeader::getPaddingSize(size_t rtp_size) const {
	if (!padding) {
		return 0;
	}
	auto end = (uint8_t*)this + rtp_size - 1;
	return *end;
}

size_t RtpHeader::getPayloadSize(size_t rtp_size) const {
	auto invalid_size = getPayloadOffset() + getPaddingSize(rtp_size);
	if (invalid_size + RtpHeader::kRtpHeaderSize >= rtp_size) {
		return 0;
	}
	return rtp_size - invalid_size - RtpHeader::kRtpHeaderSize;
}

string RtpHeader::dumpString(size_t rtp_size) const {
	stringstream st;

	st << "version:" << (int)version << "\r\n";
	st << "padding:" << getPaddingSize(rtp_size) << "\r\n";
	st << "ext:" << getExtSize() << "\r\n";
	st << "csrc:" << getCsrcSize() << "\r\n";
	st << "mark:" << (int)mark << "\r\n";
	st << "pt:" << (int)pt << "\r\n";
	st << "seq:" << ntohs(seq) << "\r\n";
	st << "stamp:" << ntohl(stamp) << "\r\n";
	st << "ssrc:" << ntohl(ssrc) << "\r\n";
	st << "rtp size:" << rtp_size << "\r\n";
	st << "payload offset:" << getPayloadOffset() << "\r\n";
	st << "payload size:" << getPayloadSize(rtp_size) << "\r\n";
	
	return st.str();
}
/////////////////////////////////////////////////////////

int nalu_buff_find(const uint8_t* ptr, int len, int* nalsplit_len)
{
	int i = 0;
	for (i = 0; i < len - 3; i++)
	{
		if (i < len - 4 && memcmp(ptr + i, "\x00\x00\x00\x01", 4) == 0)
		{
			if (nalsplit_len)
				*nalsplit_len = 4;
			return i;
		}
		if (i < len - 3 && memcmp(ptr + i, "\x00\x00\x01", 3) == 0)
		{
			if (nalsplit_len)
				*nalsplit_len = 3;
			return i;
		}
	}
	return -1;
}

///////////////////////////////////////////////////
#include "mpeg-ps.h"
#include "mpeg-ts-proto.h"

static int _CONFIG_RTP_PT_PS = 96;
static int _CONFIG_RTP_PT_H264 = 98;
static int _CONFIG_RTP_PT_H265 = 97;

inline const char* ftimestamp(int64_t t, char* buf)
{
	if (PTS_NO_VALUE == t)
	{
		sprintf(buf, "(null)");
	}
	else
	{
		t /= 90;
		sprintf(buf, "%d:%02d:%02d.%03d", (int)(t / 3600000), (int)((t / 60000) % 60), (int)((t / 1000) % 60), (int)(t % 1000));
	}
	return buf;
}

struct file_out
{
	FILE* fp;
};


static int onpacket(void* param, int /*stream*/, int avtype, int flags, int64_t pts, int64_t dts, const void* data, size_t bytes)
{
	static char s_pts[64], s_dts[64];
	struct file_out* fout = (struct file_out*)param;

	if (PSI_STREAM_AAC == avtype)
	{
		static int64_t a_pts = 0, a_dts = 0;
		if (PTS_NO_VALUE == dts)
			dts = pts;
		//assert(0 == a_dts || dts >= a_dts);
		printf("[A] pts: %s(%lld), dts: %s(%lld), diff: %03d/%03d\n", ftimestamp(pts, s_pts), pts, ftimestamp(dts, s_dts), dts, (int)(pts - a_pts) / 90, (int)(dts - a_dts) / 90);
		a_pts = pts;
		a_dts = dts;

		//fwrite(data, 1, bytes, afp);
	}
	else if (PSI_STREAM_H264 == avtype)
	{
		//如何知道是H264还是H265
		static int64_t v_pts = 0, v_dts = 0;
		//assert(0 == v_dts || dts >= v_dts);
		printf("[V.H264] pts: %s(%lld), dts: %s(%lld), diff: %03d/%03d, size: %u\n", ftimestamp(pts, s_pts), pts, ftimestamp(dts, s_dts), dts, (int)(pts - v_pts) / 90, 
			(int)(dts - v_dts) / 90, bytes);
		v_pts = pts;
		v_dts = dts;

		if (fout && fout->fp) {
			fwrite(data, 1, bytes, fout->fp);
			fflush(fout->fp);
		}
	}
	else
	{
		//assert(0);

		static int64_t a_pts = 0, a_dts = 0;
		if (PTS_NO_VALUE == dts)
			dts = pts;
		//assert(0 == a_dts || dts >= a_dts);
		printf("type=%X, [A] pts: %s(%lld), dts: %s(%lld), diff: %03d/%03d, size:%lu\n", avtype, 
			ftimestamp(pts, s_pts),			
			pts, ftimestamp(dts, s_dts), dts, 
			(int)(pts - a_pts) / 90, 
			(int)(dts - a_dts) / 90,
			bytes);
		int i = 0;
		const uint8_t* ptr = (const uint8_t*)data;

		for (i = 0; i < bytes; i++)
			printf("%02X ", ptr[i]);
		printf("\n");
		a_pts = pts;
		a_dts = dts;
	}

	return 0;
}

static void mpeg_ps_dec_testonstream(void* param, int stream, int codecid, const void* extra, int bytes, int finish)
{
	printf("stream %d, codecid: %d(%02X), finish: %s\n", stream, codecid, codecid, finish ? "true" : "false");
}

struct buffcache {
	uint8_t* buff;
	int size;
	int cur_index;
};

struct buffcache* bufcache_alloc(int size) {
	struct buffcache* cache = (struct buffcache*)malloc(sizeof(struct buffcache) + size);
	cache->buff = ((uint8_t*)cache) + sizeof(struct buffcache);
	cache->size = size;
	cache->cur_index = 0;
	return cache;
}

int bufcache_write(struct buffcache* c, void* data, int size)
{
	memcpy(c->buff + c->cur_index, data, size);
	c->cur_index += size;
	return c->cur_index;
}

int bufcache_pop(struct buffcache* c, int size) {
	int move_size = 0;
	move_size = size > c->cur_index ? c->cur_index : size;
	c->cur_index -= move_size;
	memcpy(c->buff, c->buff + move_size, c->cur_index);
	return move_size;
}

extern "C" {
	#include "librtp/include/rtp-packet.h"
	//#include "librtp/include/rtp-payload.h"
#include "librtp/payload/rtp-payload-internal.h"
}

static void* rtp_alloc(void* /*param*/, int bytes)
{
	//static uint8_t buffer[2 * 1024 * 1024 + 4] = { 0, 0, 0, 1, };
	//return buffer + 4;
	return 0;
}

static void rtp_free(void* /*param*/, void* /*packet*/)
{
}

static int rtp_decode_packet(void* param, const void* packet, int bytes, uint32_t timestamp, int flags)
{
	static const uint8_t start_code[4] = { 0, 0, 0, 1 };
	struct file_out* fout = (struct file_out*)param;

	if (fout&& fout->fp) {
		fwrite(start_code, 1, sizeof(start_code), fout->fp);
		fwrite(packet,1, bytes, fout->fp);
		fflush(fout->fp);
	}
	printf("out decode packet\n");
	return 0;
}

struct rtp_payload_t rtp_handler_out= {
	rtp_alloc,
	rtp_free,
	rtp_decode_packet
};

struct rtp_decode_ctx {
	ps_demuxer_t* ps;
	void* decoder_h264;
	void* decoder_h265;
};

int handle_rtp_h264(struct rtp_decode_ctx *ctx, uint8_t* packet, size_t bytes)
{
	//printf("H264\n"); //H264 RTP传输
	rtp_h264_decode()->input(ctx->decoder_h264, packet, bytes);
	return 0;
}

int handle_rtp_h265(struct rtp_decode_ctx* ctx, uint8_t* packet, size_t bytes)
{
	//printf("H265\n"); //H265 RTP传输
	rtp_h265_decode()->input(ctx->decoder_h265, packet, bytes);
	return 0;
}

int handleOneRtp(struct rtp_decode_ctx* ctx, struct buffcache* cache, uint8_t* ptr, size_t len)
{
	RtpHeader* header = (RtpHeader*)ptr;
	static int seq = -1;

	if (header->version != RtpHeader::kRtpVersion) {
		std::cout << "非法的rtp，version字段非法" << std::endl;
		return 0;
	}
	if (!header->getPayloadSize(len)) {
		//无有效负载的rtp包
		return false;
	}
	if (seq == header->seq)
	{
		std::cout << "重复的数据包" << std::endl;
		//return false;
	}
	if (seq == -1)
		seq = header->seq;

	//比对缓存ssrc
	auto ssrc = ntohl(header->ssrc);
	
	//string str=header->dumpString(len);
	//std::cout << str << std::endl;

	auto payload = header->getPayloadData();
	auto size = header->getPayloadSize(len);
	auto stamp = ntohl(header->stamp) * uint64_t(1000) / 90000;  //header->getStampMS();

	//auto seq = header->seq;
	//_buffer.append((char*)payload, size);
	//时间戳发生变化或者缓存超过MAX_FRAME_SIZE，则清空上帧数据

	//PS解码器  _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ps, _interface);  std::make_shared<PSDecoder>()
	//ps_demuxer_input
	if (_CONFIG_RTP_PT_PS == header->pt)
	{
		bufcache_write(cache, payload, size);

		int ret = ps_demuxer_input(ctx->ps, cache->buff, cache->cur_index);
		//
		////解析成功全部或部分
		if (ret > 0) {
			//解析成功全部或部分
			//payload + ret; //剩下部分,放入缓存中
			//uint8_t* oth_ptr = payload + ret;
			//int oth_size = size - ret;
			bufcache_pop(cache, ret);
			//printf("ps decode ret=%d\r\n", ret);
		}

		int next_index = payload + size - ptr;

		ptr += next_index;
		len -= next_index;

		if (len > 0)
			printf("other:%d\n", len);
	}

	//
	if (_CONFIG_RTP_PT_H264 == header->pt)
	{
		handle_rtp_h264(ctx, ptr, len);		
	}
	//
	if (_CONFIG_RTP_PT_H265 == header->pt)
	{
		handle_rtp_h265(ctx, ptr, len);
	}
}

static DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	MyInfo* param = (MyInfo*)lpParam;

	//param->ip
	int sockfd = -1;
	if ((sockfd = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
		std::cout << "创建套接字失败:" << WSAGetLastError()<<endl;	
		return -1;
	}

	int opt = 1;
	int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, static_cast<socklen_t>(sizeof(opt)));
	if (ret == -1) {
		std::cout << "设置 SO_REUSEADDR 失败!" << endl;
	}
	unsigned long  ul = 1;
	ret = ioctlsocket(sockfd, FIONBIO, &ul); //设置为非阻塞模式

	int size = 262144;
	ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(size));

	size = 262144;
	ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(size));

	if (-1 == bindSock(sockfd, param->ip, param->port))
	{
		std::cout << "bindSock 失败!" << endl;
		return -1;
	}
	
	std::cout << "启动接收:" << param->ip << ":" << param->port << std::endl;
	int nread = 0;
	struct sockaddr addr;
	socklen_t len = sizeof(struct sockaddr);

	int data_size = 1024 * 1024;
	uint8_t* data = (uint8_t*)malloc(data_size);
	if (data == NULL)
	{
		printf("malloc err\n");
		return -1;
	}
	struct file_out fout_h264;
	struct file_out fout_h265;
	struct file_out fout_h26x_ps;

	fout_h26x_ps.fp = fopen("rtp_out_ps.h26x", "wb");
	fout_h264.fp = fopen("rtp_out.h264", "wb");
	fout_h265.fp = fopen("rtp_out.h265", "wb");

	struct ps_demuxer_notify_t notify = {
	   mpeg_ps_dec_testonstream,
	};
	ps_demuxer_t* ps = ps_demuxer_create(onpacket, &fout_h26x_ps);
	ps_demuxer_set_notify(ps, &notify, NULL);

	struct buffcache* cache = bufcache_alloc(1024 * 1024 * 2); //2M
	
	uint8_t* data_tmp = NULL;
	MSG msg;   //增加一个MSG的变量msg来接收消息

	struct rtp_decode_ctx ctx;
	memset(&ctx, 0, sizeof(ctx));
	ctx.ps = ps;
	
	ctx.decoder_h264 = rtp_h264_decode()->create(&rtp_handler_out, &fout_h264);
	ctx.decoder_h265 = rtp_h265_decode()->create(&rtp_handler_out, &fout_h265);

	while (true)
	{		
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {      //将消息队列里的消息逐个读入msg
			if (msg.message == WM_QUIT) {     //如果收到终止消息则退出
				break;
			}
			else {
				DispatchMessage(&msg);//字面意思，不解释
			}
		}
		nread = recvfrom(sockfd, (char*)data, data_size, 0, &addr, &len);

		if (nread <= 0){
			if (WSAGetLastError() == WSAETIMEDOUT || WSAEWOULDBLOCK == WSAGetLastError())
			{
				Sleep(1);
				continue;
			}

			std::cout << "nread=-1, " << WSAGetLastError() << endl;
			break;
		}

		data_tmp = data;
		if (data_tmp[0] == '$') {//RTSP
			data_tmp += 4;
			nread -= 4;
		}
		
		handleOneRtp(&ctx, cache, data_tmp, nread);
	}

	std::cout << "退出" << endl;
	ps_demuxer_destroy(ps);

	rtp_h264_decode()->destroy(ctx.decoder_h264);
	rtp_h265_decode()->destroy(ctx.decoder_h265);

	if (fout_h26x_ps.fp)
		fclose(fout_h26x_ps.fp);

	if(fout_h264.fp)
		fclose(fout_h264.fp);

	if(fout_h265.fp)
		fclose(fout_h265.fp);

	closesocket(sockfd);
	delete param;
	return 0;
}

void GB28181Dialog::OnBnClickedButtonStart()
{
	CString ipaddr;

	UpdateData(TRUE);

	_CONFIG_RTP_PT_PS = m_RTPPSPT;
	_CONFIG_RTP_PT_H264 = m_RTP_PT_H264;
	_CONFIG_RTP_PT_H265 = m_RTP_PT_H265;

	m_myips.GetWindowText(ipaddr);
	if (ipaddr.GetLength() == 0)
		return;

	if (m_pthread == NULL)
	{
		USES_CONVERSION;
		char* mip = T2A(ipaddr.GetBuffer(0));
		int port = m_port;

		MyInfo* param = new MyInfo(mip, port);
		m_pthread = AfxBeginThread((AFX_THREADPROC)ThreadProc, param, THREAD_PRIORITY_IDLE);
		ipaddr.ReleaseBuffer();

		if (m_pthread == NULL) 
		{
			delete param;
			return;
		}
		SetDlgItemText(IDC_BUTTON_START, L"停止");		
	}
	else 
	{
		m_pthread->PostThreadMessage(WM_QUIT, 0, 0);
		m_pthread = NULL;
		SetDlgItemText(IDC_BUTTON_START, L"开始");
	}

#if 0
	HANDLE hThread = CreateThread(
		NULL,//default security attributes
		0,//use default stack size
		ThreadProc,//thread function
		param,//argument to thread function
		0,//use default creation flags
		NULL);//returns the thread identifier
	if (hThread != NULL)
		CloseHandle(hThread);
#endif

	
}
